package com.googlecode.cswish.view;

import java.io.InputStream;
import java.io.Reader;
import java.io.StringReader;
import java.io.Writer;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.log4j.Logger;
import org.apache.struts2.ServletActionContext;
import org.apache.struts2.components.Component;
import org.apache.struts2.views.freemarker.FreemarkerManager;
import org.htmlcleaner.HtmlCleaner;

import com.googlecode.cswish.model.TagScope;
import com.googlecode.cswish.struts.spring.ObjectFactory;
import com.googlecode.cswish.util.FileUtil;
import com.googlecode.cswish.util.HtmlCleanerUtil;
import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.inject.Inject;
import com.opensymphony.xwork2.util.ValueStack;

import freemarker.core.Comment;
import freemarker.template.Configuration;
import freemarker.template.SimpleHash;
import freemarker.template.Template;

/**
 * User FreeMarker engineer to load a static page
 * 
 * @author Jerry.Feng 2008-11-17
 */
public class Include extends Component {

	private static final Logger logger = Logger.getLogger(Include.class);

	public final static String INCLUDE_PAGE = "_include_page";
	
	protected String page;
	
	protected boolean preCompile = true;
	
	@Inject
	private FreemarkerManager freemarkerManager;
	
    private ObjectFactory objectFactory;
    
    private HtmlCleanerUtil cleanerUtil;

    @Inject
    public void setObjectFactory(com.opensymphony.xwork2.ObjectFactory objectFactory) {
    	this.objectFactory = (ObjectFactory) objectFactory;
    	
    	this.cleanerUtil = this.objectFactory.getBean(HtmlCleanerUtil.class, false);
    }
	
    public String getPage() {
		return page;
	}
    
	public void setPage(String page) {
		this.page = page;
	}
	
	public boolean isPreCompile() {
		return preCompile;
	}

	public void setPreCompile(boolean preCompile) {
		this.preCompile = preCompile;
	}

	public Include(ValueStack stack) {
		super(stack);
	}
	
	@SuppressWarnings("unchecked")
	public boolean start(Writer writer) {
		stack.getContext().put(INCLUDE_PAGE, page);
		
        return super.start(writer);
    }

	@SuppressWarnings("unchecked")
	public boolean end(Writer writer, String body) {
		stack.getContext().put(INCLUDE_PAGE, null);
		
		String content = null;
		if (preCompile) {
			// load all the extend point
			List<String> extendPoints = Mark.getExtensionNames(stack, page);
			
			// load html, use <@s.mark/> to replace these extend point
			cleanerUtil.include(page);
			
			for (String target : extendPoints) {
				char c = target.charAt(target.length() - 1);
				
				Object obj = Mark.getExtension(stack, target);
				String newContent;
				if (obj instanceof Comment) {
					Comment comment = (Comment) obj;
					newContent = comment.getText();
				} else {
					newContent = "<@cs.mark name='" + target + "'/>";
				}
				
				TagScope tagScope = TagScope.valueOf(c);
				if (tagScope != TagScope.INNER) {
					target = target.substring(0, target.length() - 1);
				}
				cleanerUtil.replace(target, newContent, tagScope);
			}
			
			//cleanerUtil.replace("//debug", "", TagScope.INNER);
			// call FreeMarker engine to parse the html
			content = cleanerUtil.toString();
			
			if (logger.isDebugEnabled()) {
				logger.debug("Include page: " + page);
			}
		} else {
			InputStream input = this.getClass().getResourceAsStream(page);
			content = FileUtil.readFileToString(input, HtmlCleaner.DEFAULT_CHARSET);
			
			if (logger.isDebugEnabled()) {
				logger.debug("Include page: " + page);
			}
		}
		try {
			renderTemplate(writer, content);
		} catch (Exception e) {
			logger.error("error when rendering", e);
        }
		
		popComponentStack();
		return false;
	}
	
	public void renderTemplate( Writer writer, String content) throws Exception {
    	// get the various items required from the stack
        ValueStack stack = getStack();
        Map context = stack.getContext();
        ServletContext servletContext = (ServletContext) context.get(ServletActionContext.SERVLET_CONTEXT);
        HttpServletRequest req = (HttpServletRequest) context.get(ServletActionContext.HTTP_REQUEST);
        HttpServletResponse res = (HttpServletResponse) context.get(ServletActionContext.HTTP_RESPONSE);

        // prepare freemarker
        Configuration config = freemarkerManager.getConfiguration(servletContext);

        // TODO: cache it?
        Reader reader = new StringReader(content);
        // TODO: more page sources
		Template template = new Template(page, reader, null);

        ActionInvocation ai = ActionContext.getContext().getActionInvocation();

        Object action = (ai == null) ? null : ai.getAction();
        SimpleHash model = freemarkerManager.buildTemplateModel(stack, action,
        		servletContext, req, res, config.getObjectWrapper());

        model.put("tag", this);
        model.put("themeProperties", new HashMap(0));

        try {
            stack.push(this);
            template.process(model, writer);
        } finally {
            stack.pop();
        }
    }
}